package com.parent.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.fasterxml.jackson.databind.node.ValueNode;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import lombok.experimental.Accessors;
import lombok.extern.slf4j.Slf4j;
import okhttp3.*;
import org.springframework.util.DigestUtils;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.time.*;
import java.util.*;


/**
 * 基于okhttp and hutool and lombok
 * 不使用hutool 发起请求的原因 ：高并发下 发起过多会出现 发起请求会断开之前发起过的请求连接
 *
 * @author yan
 * @date 2024/4/6 7:56
 */
@Slf4j
public class HttpUtils {
    enum HttpMethod {
        GET, POST, PUT, DELETE
    }

    /**
     * url 拼接
     *
     * @param url
     * @param params
     * @return
     */
    @SneakyThrows
    private static String urlJoin(String url, Map<String, Object> params) {
        if (ObjectUtil.isNotEmpty(params)) {
            StringBuffer append = new StringBuffer(url);
            // 检查字符串的最后一个字符是否是"?"
            if (!url.endsWith("?")) {
                append.append("?");
            }
            params.keySet().forEach(key -> {
                Object obj = params.get(key);
                if (ObjectUtil.isNotEmpty(obj)) {
                    append.append(key).append("=").append(obj).append("&");
                }
            });
            url = append.toString();
        }
        // 检查字符串的最后一个字符是否是"&"||"?"
        if (url.endsWith("&") || url.endsWith("?")) {
            // 使用substring方法移除最后一个字符
            url = url.substring(0, url.length() - 1);
        }
        return url;
    }

    /**
     * @param builder
     * @param headers
     * @return
     */
    @SneakyThrows
    private static Request.Builder addHeader(Request.Builder builder, Map<String, String> headers) {
        if (ObjectUtil.isNotEmpty(headers)) {
            headers.keySet().forEach(key -> {
                String obj = headers.get(key);
                if (ObjectUtil.isNotEmpty(obj)) {
                    builder.addHeader(key, obj);
                }
            });
        }
        return builder;
    }

    @SneakyThrows
    public static Map<String, Object> objectToMap(Object o) {
        Map<String, Object> map = ObjectUtil.isEmpty(o) ? null : BeanUtil.beanToMap(o);
        return map;
    }

    @SneakyThrows
    public static Map<String, Object> jsonToMap(String json) {
        Map<String, Object> map = ObjectUtil.isEmpty(json) ? null : JSONUtil.toBean(json, Map.class);
        return map;
    }

    @SneakyThrows

    public static Map<String, String> jsonHeadersToMap(String json) {
        Map<String, String> map = ObjectUtil.isEmpty(json) ? null : JSONUtil.toBean(json, Map.class);
        return map;
    }

    @SneakyThrows
    public static String get(String url, String paramsJson) {
        Map<String, Object> beanToMap = jsonToMap(paramsJson);
        return get(url, beanToMap);
    }

    @SneakyThrows
    public static String post(String url, String requestBodyJson) {
        Map<String, Object> beanToMap = jsonToMap(requestBodyJson);
        return post(url, beanToMap);
    }

    @SneakyThrows
    public static String put(String url, String requestBodyJson) {
        Map<String, Object> beanToMap = jsonToMap(requestBodyJson);
        return put(url, beanToMap);
    }

    @SneakyThrows
    public static String delete(String url, String paramsJson) {
        Map<String, Object> beanToMap = jsonToMap(paramsJson);
        return delete(url, beanToMap);
    }


    @SneakyThrows
    public static String get(String url, Object params) {
        Map<String, Object> beanToMap = objectToMap(params);
        return get(url, beanToMap);
    }

    @SneakyThrows
    public static String post(String url, Object requestBody) {
        Map<String, Object> beanToMap = objectToMap(requestBody);
        return post(url, beanToMap);
    }

    @SneakyThrows
    public static String put(String url, Object requestBody) {
        Map<String, Object> beanToMap = objectToMap(requestBody);
        return put(url, beanToMap);
    }

    @SneakyThrows
    public static String delete(String url, Object params) {
        Map<String, Object> beanToMap = objectToMap(params);
        return delete(url, beanToMap);
    }

    @SneakyThrows
    public static String get(String url, Map<String, Object> params) {
        return get(url, params, null);
    }

    @SneakyThrows
    public static String post(String url, Map<String, Object> requestBody) {
        return post(url, null, requestBody, null, null);
    }

    @SneakyThrows
    public static String put(String url, Map<String, Object> requestBody) {
        return put(url, null, requestBody, null, null);
    }

    @SneakyThrows
    public static String delete(String url, Map<String, Object> params) {
        return delete(url, params, null);
    }

    @SneakyThrows
    public static String get(String url, Map<String, Object> params, Map<String, String> headers) {
        HttpMethod get = HttpMethod.GET;
        return sendHttpRequest(get, url, params, null, headers, null);
    }

    @SneakyThrows
    public static String post(String url, Map<String, Object> params, Map<String, Object> requestBody, Map<String, String> headers, String mediaType) {
        HttpMethod post = HttpMethod.POST;
        return sendHttpRequest(post, url, params, requestBody, headers, mediaType);
    }

    @SneakyThrows
    public static String put(String url, Map<String, Object> params, Map<String, Object> requestBody, Map<String, String> headers, String mediaType) {
        HttpMethod put = HttpMethod.PUT;
        return sendHttpRequest(put, url, params, requestBody, headers, mediaType);
    }

    @SneakyThrows
    public static String delete(String url, Map<String, Object> params, Map<String, String> headers) {
        HttpMethod delete = HttpMethod.DELETE;
        return sendHttpRequest(delete, url, params, null, headers, null);
    }


    @SneakyThrows
    public static String sendHttpRequest(HttpMethod httpMethod, String url, String paramsJson, String requestBodyJson, String headersJson, String mediaType) {
        Map<String, Object> paramsToMap = jsonToMap(paramsJson);
        Map<String, Object> requestBodyToMap = jsonToMap(requestBodyJson);
        Map<String, String> headersToMap = jsonHeadersToMap(headersJson);
        return sendHttpRequest(httpMethod, url, paramsToMap, requestBodyToMap, headersToMap, mediaType);
    }

    @SneakyThrows
    public static String sendHttpRequest(HttpMethod httpMethod, String url, Object params, Object requestBody, Object headers, String mediaType) {
        Map<String, Object> paramsToMap = objectToMap(params);
        Map<String, Object> requestBodyToMap = objectToMap(requestBody);
        Map<String, Object> headersToMapTemp = objectToMap(headers);
        Map<String, String> headersToMap = ObjectUtil.isEmpty(headers) ? null : new LinkedHashMap<>();
        if (ObjectUtil.isNotEmpty(headers))
            headersToMapTemp.keySet().stream().forEach(key -> headersToMap.put(key, String.valueOf(headersToMapTemp.get(key))));
        return sendHttpRequest(httpMethod, url, paramsToMap, requestBodyToMap, headersToMap, mediaType);
    }

    /**
     * 发送请求(该类最底层)
     *
     * @param httpMethod  请求方式
     * @param url         请求地址
     * @param params      请求行
     * @param requestBody 请求体
     * @param headers     请求头
     * @param mediaType   请求格式
     * @return 响应
     */
    @SneakyThrows
    public static String sendHttpRequest(HttpMethod httpMethod, String url, Map<String, Object> params, Map<String, Object> requestBody, Map<String, String> headers, String mediaType) {
        Request.Builder builder = addHeader(new Request.Builder(), headers).url(urlJoin(url, params));
        if (Objects.equals(HttpMethod.GET, httpMethod)) {
            builder.get();
        } else if (Objects.equals(HttpMethod.POST, httpMethod) || Objects.equals(HttpMethod.PUT, httpMethod)) {
            String jsonStr = JSONUtil.toJsonStr(ObjectUtil.isNotEmpty(requestBody) ? requestBody : new LinkedHashMap<>());
            RequestBody body = RequestBody.create(MediaType.parse(ObjectUtil.isEmpty(mediaType) ? "application/json" : mediaType), jsonStr);
            if (Objects.equals(HttpMethod.POST, httpMethod)) builder.post(body);
            if (Objects.equals(HttpMethod.PUT, httpMethod)) builder.put(body);
        } else if (Objects.equals(HttpMethod.DELETE, httpMethod)) {
            builder.delete();
        } else {
            throw new Exception("请求方法不存在!");
        }
        Request request = builder.build();
        OkHttpClient client = new OkHttpClient();
        Response response = client.newCall(request).execute();
        if (!response.isSuccessful()) {
            log.error("Error: code={},message={}", response.code(), response.message());
        }
        String responseData = response.body().string();
        return responseData;
    }


    static ObjectMapper objectMapper = new ObjectMapper();
    /**
     * 排序
     *
     * @param map
     * @return
     */
    public static  Map<String, Object> fieldForSort(Map<String, String[]> map) {
        JsonNode jsonNode = objectMapper.valueToTree(map);
        return fieldForSort(jsonNode);
    }

    public static Map<String, Object> fieldForSort(JsonNode jsonNode) {
        Map<String, Object> fieldMap = new LinkedHashMap<>();
        fieldForSort(jsonNode, fieldMap);
        return fieldMap;
    }



    // 定义一个方法 fieldForSort，从 JSON 节点中提取字段，并将它们存储在一个映射中。
    public static Map<String, Object> fieldForSort(JsonNode jsonNode, Map<String, Object> fieldMap) {
        // 判断 jsonNode 是否为 ArrayNode 类型
        if (jsonNode instanceof ArrayNode) {
            // 遍历数组节点下的所有元素，并对每个元素递归调用 fieldForSort 方法
            for (JsonNode item : jsonNode) {
                fieldForSort(item, fieldMap);
            }
        } else if (jsonNode instanceof ObjectNode) {
            // 遍历对象节点下的所有字段
            Iterator<Map.Entry<String, JsonNode>> iterator = jsonNode.fields();
            while (iterator.hasNext()) {
                Map.Entry<String, JsonNode> entry = iterator.next();
                String fieldName = entry.getKey();
                JsonNode fieldValue = entry.getValue();
                // 如果字段的值是 ValueNode 类型
                if (fieldValue instanceof ValueNode) {
                    // 将该字段名和字段值添加到 fieldMap 中
                    fieldMap.put(fieldName, fieldValue.asText());
                } else if (fieldValue instanceof ObjectNode) {
                    // 如果字段的值是 ObjectNode 类型，则递归调用 fieldForSort 方法
                    fieldForSort(fieldValue, fieldMap);
                } else if (fieldValue instanceof ArrayNode) {
                    // 如果字段的值是 ArrayNode 类型，则遍历该数组节点下的所有元素，并对每个元素递归调用 fieldForSort 方法
                    for (JsonNode arrayItem : fieldValue) {
                        fieldForSort(arrayItem, fieldMap);
                    }
                }
            }
        }
        // 返回 fieldMap
        return fieldMap;
    }

    /**
     * @param salt   签名密钥
     * @param method HTTP方法（GET、POST等）
     * @param url    请求的URL
     * @param map    请求参数的Map
     * @return 生成的签名字符串
     */
    @SneakyThrows
    public static String generalSign(String salt, String method, String url, Map<String, String[]> map, Collection<String> exCollection) {
        // 将排除参数转换为List，方便后续操作
        List<String> exList = CollUtil.isNotEmpty(exCollection) ? CollUtil.newArrayList() : CollUtil.newArrayList(exCollection);
        // 构建用于生成签名的字符串
        StringBuilder stringBuilder = new StringBuilder().append(method).append(url);
        // 使用TreeMap对请求参数进行排序，保证签名的一致性
        TreeMap<String, Object> treeMap = new TreeMap<>(fieldForSort(map));
        // 遍历排序后的请求参数，将参数名和值拼接成字符串
        treeMap.keySet().stream().filter(e -> !exList.contains(e))
                .forEach(key -> stringBuilder.append(key).append("=").append(treeMap.get(key).toString()));
        // 追加签名密钥的属性值
        stringBuilder.append(salt);
        String preContent = stringBuilder.toString().replaceAll("\\\\+n", "\n").replaceAll("\\\\+r", "\r");
        // 对签名字符串进行URL编码
        byte[] bytes = URLEncoder.encode(preContent.toString(), StandardCharsets.UTF_8.name())
                .replace("+", "%20")
                .replace("*", "%2A")
                .getBytes();
        // 计算MD5摘要，返回十六进制字符串形式的摘要
        return DigestUtils.md5DigestAsHex(bytes);
    }

    public static void main(String[] args) {
        String aLong  = System.currentTimeMillis()/1000 + "";

        LocalDateTime localDateTime1 = LocalDateTime.ofInstant(Instant.ofEpochSecond(Long.parseLong(aLong)),
                ZoneId.systemDefault());
        LocalDateTime now = LocalDateTime.now();
        Duration between1 = Duration.between(aLong == null ? LocalDateTime.ofInstant(Instant.EPOCH, ZoneId.systemDefault()) :
                localDateTime1, now);
        long l = between1.toMinutes();
        System.out.println(l);

        long time = new Date().getTime();
        long timestamp  = System.currentTimeMillis()/1000;

        String url = "http://localhost:8800/seata/jwt/operateLog/page";
        LinkedHashMap<String, String> headers = new LinkedHashMap<>();
        headers.put("timestamp", timestamp+"");
        headers.put("Authorization", "eyJhbGciOiJIUzI1NiJ9.eyJqdGkiOiIzNTNhMGEyOTkwY2I0YTNjYTc2MDlmM2U2ZTMzOTEwZiIsInN1YiI6Ii0xIiwiaXNzIjoieWFuIiwiaWF0IjoxNzE2Mjg0NjE4LCJleHAiOjE3MTYzNzEwMTh9.BRTKURCCKS3_4yUu73S_1UG_7Q-6zdqhfdS4KoXU7MI");
        String apiSalt = "API_SALT";
        String get = "GET";
        HashMap<String, Object> map = new HashMap<>();
        map.put("page", 2);
        HashMap<String, String[]> hashMap = new HashMap<>();
        hashMap.put("page", new String[]{"2"});
        String sign = generalSign(apiSalt, get, url, hashMap, new ArrayList<>());
        headers.put("sign", sign);

        LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
        System.out.println(localDateTime);
        Duration between = Duration.between(localDateTime, LocalDateTime.now());
        long a = between.toMinutes();
        System.out.println(a);
        // 将 LocalDateTime 转换为 long
//        ZoneId zoneId  = ZoneId.systemDefault();

//        ZoneOffset zoneOffset = ZoneOffset.ofTotalSeconds(Integer.parseInt(zoneId.getId()));
         timestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.of("+8"));
//        headers.put("timestamp", String.valueOf(timestamp));
        System.out.println(timestamp);
        LocalDateTime startInclusive = LocalDateTime.ofInstant(Instant.ofEpochSecond(timestamp), ZoneId.systemDefault());
        System.out.println(startInclusive);

        String s = get(url, map, headers);
        System.out.println(s);


//        LinkedHashMap<String, Object> map = new LinkedHashMap<>();
//        map.put("q", "java");
//        String paramsJson = "{\n" +
//                "    \"q\": \"java\"\n" +
//                "}";
//        @Data
//        @NoArgsConstructor
//        @AllArgsConstructor
//        @Accessors(chain = true)
//        class a {
//            Object q;
//        }
//        String re = HttpUtils.get("http://localhost:9800/tree/api/tree/page", "");
//        System.out.println(re);
//
//        String json = "{\n" +
//                "    \"name\": \"java\"\n" +
//                "}";
//        String post = post("http://localhost:9800/tree/api/tree/add", json);
//        System.out.println(post);
//        String s = "{\"id\":3,\"name\":\"java\",\"parentId\":null}";
//        String put = put("http://localhost:9800/tree/api/tree/update", s);
//        System.out.println(put);
//        s = "{\"treeIdStr\":3}";
//        String delete = delete("http://localhost:9800/tree/api/tree/delete", s);
//        System.out.println(delete);

    }
}
